# iRotorDDE lets certain satellite communications software rotate antennas
# that are driven with iRotor Orbit360 hack by Tony Monteiro, AA2TX
# These include:
#  * Ham Radio Deluxe
#  * SatPC32
#  * Nova

# It works by picking up a channel of information called a DDE from one of these
# programs and sending the appropriate azimuth and elevation to Tony's code.
# Therefore, your first step in installing this software is to ensure that 
# AA2TX's software is installed and works on its own
# Your second step is to edit the variables below

# iRotorDDE does *not* perform any sort of radio tuning.
package require dde


# CHANGE the values below to suit your circumstances:
# The path to the directory containing AA2TX's 'ROTORDRV.EXE and 'ROTOR.EXE'
set iRotorDirectory "D://Documents and Settings/Bruce/Desktop/Rotordrv/"

# The following four are parameters passed directly to 'ROTORDRV'.
# See AA2TX's software notes for futher information

# The parallel port to which the rotor controllers are connected
set rotorParallelPortNumber 1
# Is the azimuth stop at the south 'S' or north 'N'?
set stopPole "S"
# Should this program control the azimuth only 'AzOnly' or 'Both' rotors
set rotorMode "Both"
# Should the 'flip mode' be used by the rotor? This will send the elevation 
# beyond 90 deg. if that entails a shorter trip to the next pointing
# use values "on" and "off"
set flip "on"
# When a new satellite is selected, how long should the program wait before
# starting to turn the antennas in its direction?
set waitInMilliseconds 5000

# END of user-edited variables
# Do not edit the variables below

# Please ignore for this version of the software
set azimuthCorrection 0
set elevationCorrection 0
set bumpIgnoreThreshold 10
set degreeChange 2

set iRotorDr "${iRotorDirectory}ROTORDRV.EXE"
set iRotorExec "${iRotorDirectory}ROTOR.EXE"

# This function is not used in the current version of the software
proc degreesToEightBit {degree} {
  return [expr int ([expr ($degree *255/360)])]
}
#Give a bogus start value that will assure that we'll rotate the first time
set digAz 3000
set oldDigAz 3000
set el -3000
set elThreshold -5
set oldSatName "StartingUp"

# Binary function determining if the change in satellite position warrants a 
# movement of the rotor
# This is NOT USED in the current code
proc significantShift {digAz} {
	global oldDigAz
	global degreeChange
	set difference [expr abs ([expr $oldDigAz - $digAz])]
        if {$difference >= $degreeChange} {
		return true
	} else {
		return false
	}
}

# Binary function determining if the satellite is above the horizon set by
# the 'elThreshold' value (in degrees) 
# This is NOT USED in the current code
proc inView {el} {
	global elThreshold
	if {$el >= $elThreshold} {
		return true
	}  else {
		return false
	}
}

# This is NOT USED in the current code
proc correctAz {az azimuthCorrection} {
    set initialValue [expr $az + $azimuthCorrection]
    if { $initialValue >= 0 && $initialValue <= 360 } {
	return $initialValue
    } else { 
	if {$initialValue > 360} {
	    return [expr $initialValue - 360]
	} else {
	    return [expr 360 + $initialValue]
	}
    }
}
	

proc setRotor {az el} {
	global azimuthCorrection
	global elevationCorrection
	set correctedAz  [correctAz $az $azimuthCorrection]
	set correctedEl [expr $el + $elevationCorrection]
        
	puts "++++++++++ Setting rotor to Az: $az  El: $el"
	setiRotor $az $el
}

proc testForRotorDRV {} { 
    global iRotorExec
     set out [exec $iRotorExec  "status"]
    puts "test result: $out"
    if {$out == "RotorDRV is not loaded"} {
	return false
    } else {
	return true
    }
}
proc startRotorDRV {} {
     	 global iRotorDr
	 global stopPole
	 global rotorMode
	 global rotorParallelPortNumber   
    if {[testForRotorDRV] == false} {

    set out [exec $iRotorDr $rotorParallelPortNumber $stopPole $rotorMode]  
    return "Started RotorDRV: $out"
    } else {
	return "RotorDRV already running"
    }
}

proc setiRotor {az el} {
    global iRotorExec
    set out [exec $iRotorExec "position" $az $el]
    puts $out
    set out [exec $iRotorExec "r"]
    puts $out
}

proc startiRotor {} {
    global iRotorExec
    global flip
    set out [exec $iRotorExec "track" "on"]
    puts $out
    set out [exec $iRotorExec "flip"  $flip]
    set out [exec $iRotorExec "status"]
    puts $out
}

proc processElements {az el} {
	setRotor [expr round ($az)] [expr round ($el)]
}

proc findDDEServer {} {
	puts "*******Welcome to iRotorDDE**********"
	puts "Testing for live DDE Server"
#        set satpc [ddeCallSatPC]
	set errortime [catch {set satpc [ddeCallSatPC]}]
	if {$errortime == 0} {
		puts "SatPC32 is live. It will be used as the server"
		return "SatPC32"
	} else {
	    set errortime [catch {set hrd [ddeCallHRD]}]
	    if {$errortime == 0} {
	    puts "HRD or Nova is alive. It will be used as the server"
	    return "HRD"
	} else {
		puts "No live DDE server was found. Please start one."
		return "NONE"
	       }
	}
}
	

proc ddeCall {programName} {
if {$programName == "SatPC32"} {
	return [ddeCallSatPC]
} else {
    if {$programName == "HRD"} {
	return [ddeCallHRD]
    } else {
	puts "failed to find a DDE Server"
	exit
}
}
}

proc ddeCallTrappingErrors {programName} {
        set ddeError [catch {set out [ddeCall $programName]}]
	if {$ddeError == 1} {
		puts "$programName seems to have stopped. Please restart it"
		exit
	} else {return $out}
}

proc ddeCallSatPC {} {
   set satpc [dde request "SatPC32" "SatPcDdeConv" "SatPcDdeItem"]
	   set info [split $satpc]
         puts "info: $info"
	 set azprefixed [lindex $info 1]
	 set azprefixedlength [string length $azprefixed]
	 #puts "length: $azprefixedlength"
	 set elprefixed [lindex $info 2]
	 set elprefixedlength [string length $elprefixed]
#         set az [substr $satpc " AZ" " "]
#         set el [substr $satpc " EL" " "]
#         set newSatName [substr $satpc "" " AZ"]
	 set az [string range $azprefixed 2 $azprefixedlength ]
	 set el [string range $elprefixed 2 $elprefixedlength ]
	 puts "az: $az; el: $el"
	set newSatName [lindex $info 0]
	 return [list $newSatName $az $el]
}

proc ddeCallHRD {} {
   set hrd [dde request "NFW32" "NFW_DATA" "NFW_SERVER"]
	   set info [split $hrd]
	 set azprefixed [lindex $info 1]
	 set azprefixedlength [string length $azprefixed]
	 #puts "length: $azprefixedlength"
	 set elprefixed [lindex $info 2]
	 set elprefixedlength [string length $elprefixed]
	 set az [string range $azprefixed 3 $azprefixedlength ]
	 set el [string range $elprefixed 3 $elprefixedlength ]
	 puts "az: $az; el: $el"
	 set newSatName [lindex $info 0]
	 return [list $newSatName $az $el]
}

proc serverLoop {ddeServer} {
while (1) {
	global oldSatName
	global waitInMilliseconds
	set ddeList [ddeCallTrappingErrors $ddeServer]
	 set newSatName [lindex $ddeList 0]
	 set az [lindex $ddeList 1]
	 set el [lindex $ddeList 2]
	 if {$oldSatName == $newSatName} { 
		 processElements $az $el
		 after $waitInMilliseconds
	 } else  { if {$oldSatName != "StartingUp"} {
			 puts "Sat has changed from $oldSatName"
			 set newSatName [lindex [ddeCallTrappingErrors $ddeServer] 0]
			 set oldSatName $newSatName
			 puts "Now tracking $oldSatName"
		 } else {# Starting up
			 set oldSatName $newSatName
			 processElements $az $el
			 after $waitInMilliseconds
		 }
	 } 
		 
  }
}

proc findServerLoop {} {
    set serverName "NONE"
    while {$serverName == "NONE"} {
	set serverName [findDDEServer]
	after 3000
    }
    return $serverName
}


#Gui code here


 
# Main program starts here

# Check if RotorDRV is running, if not, start it with our parameters

startRotorDRV

#Set tracking to 'on'
startiRotor

# Though this is set up as a loop, the function above will exit this 
# script when the current DDE server is closed
while {true} {
    # Find a DDE server, and name it ours
    set ddeServer [findServerLoop]
    # continuously update the rotor.exe code with the positions declared in the 
    # DDE channel.
    serverLoop $ddeServer
}
# End of all code


